//.NET Framework Developer's Guide   

//Implementing a Custom Permission
//All permission objects must implement the IPermission interface. Inheriting from the CodeAccessPermission class is the easiest way to create a custom permission because CodeAccessPermission implements IPermission and provides most of the methods required for a permission. Additionally, you must implement the IUnrestrictedPermision interface for all custom code access permissions. The custom permission class is required for both imperative and declarative security support, so you should create it even if you plan to use only declarative security.

//Defining the Permission Class 
//To derive from the CodeAccessPermission class, you must override the following five key methods and provide your own implementation: 

//Copy creates a duplicate of the current permission object. 
//Intersect returns the intersection of allowed permissions of the current class and a passed class. 
//IsSubsetOf returns true if a passed permission includes everything allowed by the current permission. 
//FromXml decodes an XML representation of your custom permission. 
//ToXml encodes an XML representation of your custom permission. 
//The IUnrestrictedPermission interface requires you to override and implement a single method called IsUnrestrictedPermission. In order to support the IUnrestrictedPermission interface, you must implement some system, such as a Boolean value that represents the state of restriction in the current object, to define whether the current instance of the permission is unrestricted.

//The following code fragment illustrates the manner in which a custom permission class might be defined. A constructor that accepts a PermissionState enumeration and a Boolean value called unrestricted are both created. The PermissionState enumeration has a value of either Unrestricted or None. If the passed enumeration has a value of Unrestricted, the constructor sets unrestricted to true. Otherwise, unrestricted is set to false. In addition to constructors specific to your custom permission, all code access permissions (any permission that inherits from CodeAccessPermission) must support a constructor that takes only a PermissionState enumeration.

//In addition to the code shown in the following example, you must implement IsUnrestricted method and override the Copy, Intersect, IsSubsetOf, ToXML, and FromXML methods. For information about completing these steps, see the sections that follow the example.

//[Visual Basic]
//Option Strict
//Option Explicit
//Imports System
//Imports System.Security
//Imports System.Security.Permissions
//<SerializableAttribute()> NotInheritable Public Class CustomPermission
//   Inherits CodeAccessPermission
//   Implements IUnrestrictedPermission
//   Private unrestricted As Boolean
   
   
//   Public Sub New(state As PermissionState)
//      If state = PermissionState.Unrestricted Then
//         unrestricted = True
//      Else
//         unrestricted = False
//      End If
//   End Sub
//   'Define the rest of your custom permission here. You must 
//   'implement IsUnrestricted and override the Copy, Intersect, 
//   'IsSubsetOf, ToXML, and FromXML methods.
//End Class
//[C#]
//using System;
//using System.Security;
//using System.Security.Permissions;

//[SerializableAttribute()]
//public sealed class CustomPermission: CodeAccessPermission, IUnrestrictedPermission
//{  
//   private bool unrestricted;

//   public CustomPermission(PermissionState state)
//   {
//      if(state == PermissionState.Unrestricted)
//      {
//         unrestricted = true;
//      }
//      else
//      {
//         unrestricted = false;
//      }
//   }     

//   //Define the rest of your custom permission here. You must 
//   //implement IsUnrestricted and override the Copy, Intersect, 
//   //IsSubsetOf, ToXML, and FromXML methods.
//}
//Notice that the class is marked with SerializableAttribute. You must mark your class with SerializableAttribute in order to support declarative syntax using an attribute. For information about creating a custom attribute that uses a custom security object, see Adding Declarative Security Support.

//Implementing the IsUnrestricted Method
//The IsUnrestricted method is required by the IUnrestrictedPermission interface and simply returns a Boolean value that indicates whether the current instance of the permission has unrestricted access to the resource protected by the permission. To implement this method, simply return the value of unrestricted.

//The following code example implements the IsUnrestricted method.

//[Visual Basic]
//Public Function IsUnrestricted() As Boolean Implements IUnrestrictedPermission.IsUnrestricted
//   Return unrestricted
//End Function
//[C#]
//public bool IsUnrestricted()
//{
//   return unrestricted;
//}
//Overriding the Copy Method 
//The copy method is required by the CodeAccessPermission class and returns a copy of the current permission class.

//The following code illustrates how to override the Copy method.

//[Visual Basic]
//Public Overrides Function Copy() As IPermission
//   Dim myCopy As New CustomPermission(PermissionState.None)
   
//   If Me.IsUnrestricted() Then
//      myCopy.unrestricted = True
//   Else
//      myCopy.unrestricted = False
//   End If
//   Return myCopy
//End Function
//[C#]
//public override IPermission Copy()
//{
//   CustomPermission copy = new CustomPermission(PermissionState.None);

//   if(this.IsUnrestricted())
//   {
//      copy.unrestricted = true;
//   }
//   else
//   {
//      copy.unrestricted = false;
//   }
//   return copy;
//} 
//Overriding the Intersect and IsSubsetOf Methods
//All permissions must implement the Intersect and IsSubsetOf methods. The behavior of these operations must be implemented as follows: 

//X.IsSubsetOf(Y) is true if permission Y includes everything allowed by X. 
//X.Intersect(Y) results in a permission that allows all operations and only those operations allowed by both the X and Y permissions. 
//The following example illustrates how to override and implement the Intersect method. The method accepts a class that derives from IPermission and initializes this class to a new instance of the CustomPermisison object. In this case, the intersection of the current object and the passed object is a final object with the value of unrestricted if both objects have that value. However, if one of the two objects has a false value for unrestricted, then the final object will also have a false value for unrestricted. This code returns an unrestricted object only if both objects are unrestricted.

//[Visual Basic]
//Public Overrides Function Intersect(target As IPermission) As IPermission
//   If Nothing Is target Then
//      Return Nothing
//   End If
//   Try
//      Dim PassedPermission As CustomPermission = CType(target, CustomPermission)
//      If Not PassedPermission.IsUnrestricted() Then
//         Return PassedPermission
//      End If
//      Return Me.Copy()
//   Catch InvalidCastException As Exception
//      Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
//      End Try
//End Function
//[C#]

//public override IPermission Intersect(IPermission target)
//{
//   try
//   {
//      if(null == target)
//      {
//         return null;
//      }
//      CustomPermission PassedPermission = (CustomPermission)target;

//      if(!PassedPermission.IsUnrestricted())
//      {
//         return PassedPermission;
//      }
//      return this.Copy();
//   }
//   catch (InvalidCastException)
//   {
//      throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
//   }                
//}
//In the following example, the IsSubsetOf method is overridden. In order for this method to return true, the current instance and a passed instance must allow exactly the same set of operations. In this case, the overridden method initializes a new instance of the CustomPermission object to the passed permission object. If the unrestricted values are the same, then the method returns true. If they are not, the method returns false.

//[Visual Basic]
//Public Overrides Function IsSubsetOf(target As IPermission) As Boolean
//   If Nothing Is target Then
//      Return Not Me.unrestricted
//   End If
//   Try
//      Dim passedpermission As CustomPermission = CType(target, CustomPermission)
//      If Me.unrestricted = passedpermission.unrestricted Then
//         Return True
//      Else
//         Return False
//      End If
//   Catch InvalidCastException As Exception
//      Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
//      End Try
//End Function
//[C#]
//public override bool IsSubsetOf(IPermission target)
//{  
//   if(null == target)
//   {
//      return !this.unrestricted;
//   }
//   try
//   {        
//      CustomPermission passedpermission = (CustomPermission)target;
//      if(this.unrestricted == passedpermission.unrestricted)
//      {
//         return true;
//      }
//      else
//      {
//         return false;
//      }
//   }
//   catch (InvalidCastException)
//   {
//      throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
//   }                
//}
//Overriding the ToXml and FromXml Methods
//Permissions support XML encoding so that a permission object can be saved as XML and then another permission object can be restored to the value of the original, using the XML file. To support XML encoding, your custom permission must implement the ISecurityEncodable interface, which defines a ToXml and a FromXml method. Because both methods are implemented by CodeAccessPermission, if your custom permission class derives from CodeAccessPermission, you should override these methods.

//The content of the XML element that represents the object state is determined by the object itself. The FromXML method can use any XML representation as long as ToXML can interpret it and restore the same state. However, the containing Permission element must be of a standard form. For example, the form for CustomPermission might look like the following:

//<IPermission class="CustomPermissions.CustomPermission, CustomPermissionAssembly " version="1" Unrestricted="True">
//The IPermission element contains three attributes: 

//class: Contains the type name disambiguated by the name of the assembly that contains it. 
//version: Specifies the version of the XML encoding (not the version of the class's assembly). 
//Unrestricted: Specifies whether the permission has unrestricted rights. 
//All permissions must be encoded in an XML element called IPermission in order to be used by the common language runtime security system.

//New versions of a permission object should remain backward compatible with information persisted in XML from previous versions. The version tag provides information to a permission object about which version originally encoded the data.

//The SecurityElement class encapsulates the main functionality that you need in order to create and interact with XML-encoded permission objects. However, because the XML object model used for .NET Framework security is different from other XML object models, the SecurityElement class should not be used to generate other types of XML files. See the description of the SecurityElement class for a complete list of its members.

//The following code fragment creates an XML Permission element:

//[Visual Basic]
//Public Overrides Function ToXml() As SecurityElement
//   Dim element As New SecurityElement("IPermission")
//   Dim type As Type = Me.GetType()
//   Dim AssemblyName As New StringBuilder(type.Assembly.ToString())
//   AssemblyName.Replace(ControlChars.Quote, "'"c)
//   element.AddAttribute("class", type.FullName & ", " & AssemblyName.ToString)
//   element.AddAttribute("version", "1")
//   element.AddAttribute("Unrestricted", unrestricted.ToString())
//   Return element
//End Function
//[C#]
//public override SecurityElement ToXml()
//{
//   SecurityElement element = new SecurityElement("IPermission");
//   Type type = this.GetType();
//   StringBuilder AssemblyName = new StringBuilder(type.Assembly.ToString());
//   AssemblyName.Replace('\"', '\'');
//   element.AddAttribute("class", type.FullName + ", " + AssemblyName);
//   element.AddAttribute("version", "1");
//   element.AddAttribute("Unrestricted", unrestricted.ToString());
//   return element;
//}
//Notice that the previous example uses the StringBuilder.Replace method. Attributes in the SecurityElement class cannot contain double quotes, but some assembly name information is in double quotes. To handle this situation, the Replace method converts double quotes (") in the assembly name to single quotes (').

//The following method reads a SecurityElement object created by the previous method and sets the current value of the Unrestricted property to the one specified by the passed object. This method should ensure that any information stored by the ToXml method is retrieved.

//[Visual Basic]
//Public Overrides Sub FromXml(PassedElement As SecurityElement)
//   Dim element As String = PassedElement.Attribute("Unrestricted")
//   If Not element Is Nothing Then
//      Me.unrestricted = Convert.ToBoolean(element)
//   End If
//End Sub
//[C#]
//public override void FromXml(SecurityElement PassedElement)
//{
//   string element = PassedElement.Attribute("Unrestricted");
//   if(null != element)
//   {  
//      this.unrestricted = Convert.ToBoolean(element);
//   }
//}
using System;
using System.Text;
using System.Security;
using System.Security.Permissions;

namespace Waymex.Security
{
    [Serializable()]
    internal sealed class CustomPermission : CodeAccessPermission, IUnrestrictedPermission
    {
        private bool unrestricted;

        public CustomPermission(PermissionState state)
        {
            if (state == PermissionState.Unrestricted)
            {
                unrestricted = true;
            }
            else
            {
                unrestricted = false;
            }
        }

        public bool IsUnrestricted()
        {
            return unrestricted;
        }

        public override IPermission Copy()
        {
            //Create a new instance of CustomPermission with the current
            //value of unrestricted.
            CustomPermission copy = new CustomPermission(PermissionState.None);

            if (this.IsUnrestricted())
            {
                copy.unrestricted = true;
            }
            else
            {
                copy.unrestricted = false;
            }
            //Return the copy.
            return copy;
        }

        public override IPermission Intersect(IPermission target)
        {
            //If nothing was passed, return null.
            if (null == target)
            {
                return null;
            }
            try
            {
                //Create a new instance of CustomPermission from the passed object.
                CustomPermission PassedPermission = (CustomPermission)target;

                //If one class has an unrestricted value of false, then the
                //intersection will have an unrestricted value of false.
                //Return the passed class with the unrestricted value of false.
                if (!PassedPermission.unrestricted)
                {
                    return target;
                }
                //Return a copy of the current class if the passed one has
                //an unrestricted value of true.
                return this.Copy();
            }
            //Catch an InvalidCastException.
            //Throw ArgumentException to notify the user.
            catch (InvalidCastException)
            {
                throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
            }
        }

        public override bool IsSubsetOf(IPermission target)
        {
            //If nothing was passed and unrestricted is false,
            //then return true. 
            if (null == target)
            {
                return !this.unrestricted;
            }
            try
            {
                //Create a new instance of CustomPermission from the passed object.
                CustomPermission passedpermission = (CustomPermission)target;

                //If unrestricted has the same value in both objects, then
                //one is the subset of the other.
                if (this.unrestricted == passedpermission.unrestricted)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            //Catch an InvalidCastException.
            //Throw ArgumentException to notify the user.
            catch (InvalidCastException)
            {
                throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
            }
        }

        public override void FromXml(SecurityElement PassedElement)
        {
            //Get the unrestricted value from the XML and initialize 
            //the current instance of unrestricted to that value.
            string element = PassedElement.Attribute("Unrestricted");

            if (null != element)
            {
                this.unrestricted = Convert.ToBoolean(element);
            }
        }

        public override SecurityElement ToXml()
        {
            //Encode the current permission to XML using the 
            //SecurityElement class.
            SecurityElement element = new SecurityElement("IPermission");
            Type type = this.GetType();
            StringBuilder AssemblyName = new StringBuilder(type.Assembly.ToString());
            AssemblyName.Replace('\"', '\'');
            element.AddAttribute("class", type.FullName + ", " + AssemblyName);
            element.AddAttribute("version", "1");
            element.AddAttribute("Unrestricted", unrestricted.ToString());
            return element;
        }
    }
    [AttributeUsageAttribute(AttributeTargets.All, AllowMultiple = true)]
    internal sealed class CustomPermissionAttribute : CodeAccessSecurityAttribute
    {
        bool unrestricted = false;

        public new bool Unrestricted
        {
            get { return unrestricted; }
            set { unrestricted = value; }
        }

        public CustomPermissionAttribute(SecurityAction action)
            : base(action)
        {
        }
        public override IPermission CreatePermission()
        {
            if (Unrestricted)
            {
                return new CustomPermission(PermissionState.Unrestricted);
            }
            else
            {
                return new CustomPermission(PermissionState.None);
            }
        }
    }
}